package api

import (
	"errors"
	"fmt"
	"gitee.com/sansaniot/ssiot-core/httpmvc"
	"gitee.com/sansaniot/ssiot-core/httpmvc/model"
	"gitee.com/sansaniot/ssiot-core/httpmvc/service"
	"gitee.com/sansaniot/ssiot-core/logger"
	"gitee.com/sansaniot/ssiot-core/serrors"
	"gitee.com/sansaniot/ssiot-core/utils"
	vd "github.com/bytedance/go-tagexpr/v2/validator"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"gorm.io/gorm"
	"net/http"
	"time"
)

var DefaultLanguage = "zh-CN"

type Api struct {
	Context *gin.Context
	Logger  *logger.Helper
	Orm     *gorm.DB
	Errors  error
}

func (e *Api) AddError(err error) {
	if e.Errors == nil {
		e.Errors = err
	} else if err != nil {
		e.Logger.Error(err)
		e.Errors = fmt.Errorf("%v; %w", e.Errors, err)
	}
}

// MakeContext 设置http上下文
func (e *Api) MakeContext(c *gin.Context) *Api {
	e.Context = c
	e.Logger = GetRequestLogger(c)
	return e
}

// GetLogger 获取上下文提供的日志
func (e Api) GetLogger() *logger.Helper {
	return GetRequestLogger(e.Context)
}

// Bind 参数校验
func (e *Api) Bind(d interface{}, bindings ...binding.Binding) *Api {
	var err error
	if len(bindings) == 0 {
		bindings = constructor.GetBindingForGin(d)
	}
	for i := range bindings {
		if bindings[i] == nil {
			err = e.Context.ShouldBindUri(d)
		} else {
			err = e.Context.ShouldBindWith(d, bindings[i])
		}
		if err != nil && err.Error() == "EOF" {
			//e.Logger.Warn("request body is not present anymore. ")
			err = nil
			continue
		}
		if err != nil {
			e.AddError(err)
			break
		}
	}
	//vd.SetErrorFactory(func(failPath, msg string) error {
	//	return fmt.Errorf(`"validation failed: %s %s"`, failPath, msg)
	//})
	if err1 := vd.Validate(d); err1 != nil {
		e.AddError(err1)
	}
	return e
}

// GetOrm 获取Orm DB
func (e Api) GetOrm() (*gorm.DB, error) {
	db, err := httpmvc.GetOrm(e.Context)
	if err != nil {
		e.Logger.Error(http.StatusInternalServerError, err, "数据库连接获取失败")
		return nil, err
	}
	return db, nil
}

// MakeOrm 设置Orm DB
func (e *Api) MakeOrm() *Api {
	var err error
	if e.Logger == nil {
		err = errors.New("at MakeOrm logger is nil")
		e.AddError(err)
		return e
	}
	db, err := httpmvc.GetOrm(e.Context)
	if err != nil {
		e.Logger.Error(http.StatusInternalServerError, err, "数据库连接获取失败")
		e.AddError(err)
	}
	e.Orm = db
	return e
}

func (e *Api) MakeService(c *service.Service) *Api {
	e.MakeOrm()
	c.Log = e.Logger
	c.Orm = e.Orm
	return e
}

func (e *Api) MakeModel(mm ...*model.Model) *Api {
	e.MakeOrm()
	for i := range mm {
		mm[i].Orm = e.Orm
		mm[i].Log = e.Logger
	}
	return e
}

func (e *Api) Page(total int64, pageSize int, pageIndex int, rows interface{}) {
	var pages page
	var res interface{}
	pagesCount := (total + int64(pageSize) - 1) / int64(pageSize)

	if pageSize > 0 && pageIndex > 0 {
		pages = page{
			Records:   rows,
			Total:     total,
			PageSize:  pageSize,
			PageIndex: pageIndex,
			Pages:     pagesCount,
		}
		res = pages
	} else {
		res = rows
	}

	result := serrors.Response{
		Code:      serrors.OK,
		Message:   serrors.Msg(serrors.OK),
		Result:    res,
		Success:   true,
		Timestamp: time.Now().UnixNano() / 1e6,
	}

	e.Context.JSON(http.StatusOK, result)
}

func (e *Api) Fail(msgCode int, message ...string) {
	result := serrors.Response{}
	if msgCode == 0 {
		result = serrors.Response{
			Code:      msgCode,
			Message:   message[0],
			Result:    nil,
			Success:   false,
			Timestamp: time.Now().UnixNano() / 1e6,
		}
	} else {
		result = serrors.Response{
			Code:      msgCode,
			Message:   serrors.Msg(msgCode),
			Result:    nil,
			Success:   false,
			Timestamp: time.Now().UnixNano() / 1e6,
		}
	}

	e.Context.AbortWithStatusJSON(http.StatusOK, result)
}

func (e *Api) FailWithResult(msgCode int, rest interface{}, message ...string) {
	result := serrors.Response{}
	if msgCode == 0 {
		result = serrors.Response{
			Code:      msgCode,
			Message:   message[0],
			Result:    rest,
			Success:   false,
			Timestamp: time.Now().UnixNano() / 1e6,
		}
	} else {
		result = serrors.Response{
			Code:      msgCode,
			Message:   serrors.Msg(msgCode),
			Result:    rest,
			Success:   false,
			Timestamp: time.Now().UnixNano() / 1e6,
		}
	}

	e.Context.AbortWithStatusJSON(http.StatusOK, result)
}

func (e *Api) Ok(result interface{}) {
	res := serrors.Response{
		Code:      serrors.OK,
		Message:   serrors.Msg(serrors.OK),
		Result:    result,
		Success:   true,
		Timestamp: time.Now().UnixNano() / 1e6,
	}
	e.Context.AbortWithStatusJSON(http.StatusOK, res)
}

func (e *Api) Data(data interface{}) {
	result := serrors.Response{
		Code:      serrors.OK,
		Message:   serrors.Msg(serrors.OK),
		Result:    data,
		Success:   true,
		Timestamp: time.Now().UnixNano() / 1e6,
	}
	e.Context.AbortWithStatusJSON(http.StatusOK, result)
}

func (e Api) Translate(form, to interface{}) {
	utils.Translate(form, to)
}

type page struct {
	Records   interface{} `json:"records"`
	Total     int64       `json:"total"`
	PageSize  int         `json:"size"`
	PageIndex int         `json:"current"`
	Pages     int64       `json:"pages"`
}

//func (e *Api) BindContext(c *gin.Context, m *model.Model, q interface{}, bindings ...binding.Binding) error {
//	err := e.MakeContext(c).MakeOrm().Bind(q, bindings...).MakeModel(m).Errors
//	if err != nil {
//		e.Logger.Error(err)
//		e.Fail(serrors.Fail)
//	}
//	return err
//}
//
//func (e *Api) BindContextService(c *gin.Context, m *model.Model, s *service.Service, q interface{}, bindings ...binding.Binding) error {
//	err := e.MakeContext(c).MakeOrm().Bind(q, bindings...).MakeModel(m).MakeService(s).Errors
//	if err != nil {
//		e.Logger.Error(err)
//		e.Fail(serrors.Fail)
//	}
//	return err
//}
